package com.catazzz.springbootstudy.common.config;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.support.spring.stat.DruidStatInterceptor;
import org.springframework.aop.Advisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;

/**
 * @作者: Tao
 * @日期: 2024/12/30
 * @时间: 14:57
 * @描述: Druid 配置类 <br><pre>
 *      1. 配置Druid的Servlet，用于提供数据源监控和统计信息。
 *      2. 配置Druid的过滤器，用于拦截请求并统计SQL的执行信息。
 *      3. 配置Druid的拦截器，用于统计SQL的执行信息。
 *      4. 创建一个Advisor，将DruidStatInterceptor和DruidStatPointcut关联起来，以定义通知和切点的关系。
 */
//@Configuration
public class DruidConfig {

    /**
     * 配置Druid的Servlet
     *
     * @return ServletRegistrationBean <br><pre>
     *     用于注册 Druid 的 StatViewServlet。这个 Servlet 用于提供 Druid 数据库监控页面的访问接口
     */
    @Bean
    public ServletRegistrationBean<StatViewServlet> statViewServlet() {
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>();
        // 设置StatViewServlet作为bean的Servlet
        bean.setServlet(new StatViewServlet());
        // 添加URL映射，使得/druid/*路径下的请求都由StatViewServlet处理
        bean.addUrlMappings("/druid/*");
        // 添加初始化参数，设置允许访问的IP地址
        bean.addInitParameter("allow", "127.0.0.1");
        // 添加初始化参数，设置拒绝访问的IP地址
        bean.addInitParameter("deny", "192.168.1.1");
        // 添加初始化参数，设置登录用户名
        bean.addInitParameter("loginUsername", "admin");
        // 添加初始化参数，设置登录密码
        bean.addInitParameter("loginPassword", "123456");
        // 添加初始化参数，设置是否允许重置数据源配置
        bean.addInitParameter("resetEnable", "false");
        return bean;
    }

    /**
     * 配置Druid的过滤器
     * @return FilterRegistrationBean <br>
     * 用于注册 Druid 的 WebStatFilter。这个过滤器用于统计 Web 请求的相关信息，并将其记录下来，以便进行性能监控和分析
     */
    @Bean
    public FilterRegistrationBean<WebStatFilter> statFilter() {
        FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean<>();
        // 设置Web统计过滤器
        bean.setFilter(new WebStatFilter());
        // 添加URL模式，指定过滤器应用于所有请求
        bean.addUrlPatterns("/*");
        // 添加初始化参数，排除静态资源和特定路径的监控
        bean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return bean;
    }

    /**
     * 配置Druid的拦截器 spring监控
     * @return DruidStatInterceptor <br><pre>
     *      主要用于统计 SQL 执行的时间和次数等信息。它可以帮助开发者监控数据库的性能，具体作用包括：<pre>
     *          1.SQL 监控：拦截 SQL 请求，记录 SQL 语句的执行时间、执行频率等。<pre>
     *          2.性能分析：通过统计信息，帮助分析数据库操作的性能瓶颈。<pre>
     *          3.日志记录：可以配置将 SQL 执行的相关信息记录到日志中，便于后续审计和排查问题 <pre>
     */
    @Bean
    public DruidStatInterceptor druidStatInterceptor() {
        return new DruidStatInterceptor();
    }

    /**
     * 创建一个针对Druid统计功能的切点
     * @return JdkRegexpMethodPointcut <br><pre>
     *     该方法定义了哪些包下的方法执行时需要进行Druid统计 <pre>
     *     这里关注的是controller、service和mapper
     */
    @Bean
    public JdkRegexpMethodPointcut druidStatPointcut() {
        JdkRegexpMethodPointcut druidStatPointcut = new JdkRegexpMethodPointcut();

        // 定义需要进行Druid统计的包和类的正则表达式模式
        // 这里关注的是controller、service和mapper层的方法
        String[] patterns = new String[]{
                "com.catazzz.springbootstudy.*.controller.*",
                "com.catazzz.springbootstudy.*.service.*",
                "com.catazzz.springbootstudy.*.mapper.*",
        };
        druidStatPointcut.setPatterns(patterns);
        return druidStatPointcut;
    }

    /**
     * 创建一个Advisor，将DruidStatInterceptor和DruidStatPointcut关联起来
     * @return Advisor <br><pre>
     *    Advisor的作用是定义一个通知（Advice）和一个切点（Pointcut）之间的关系。具体来说：<pre>
     *      Advice：定义了在切点处要执行的逻辑，例如前置通知、后置通知、环绕通知等。<pre>
     *      Pointcut：定义了在哪些连接点（Join Point）上应用通知。<pre>
     *    Advisor将这两者结合起来，使得AOP框架可以根据定义的切点，在特定的方法执行时应用相应的通知逻辑<pre>
     */
    @Bean
    public Advisor druidStatAdvisor() {
        return new DefaultPointcutAdvisor(druidStatPointcut(), druidStatInterceptor());
    }
}
